package com.qunar.train.mock.config;

import com.google.common.io.Files;

import com.qunar.train.mock.util.AegisLog;
import com.qunar.train.mock.util.ConfigHandle;
import com.qunar.train.mock.util.SpringUtil;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

public class MockQconfig {

    public static final String QCONFIG_TEST_PATH = "qconfig_test/";
    public static final String QCONFIG_MOCK_SWITCH = "qconfig.mock.switch";
    public static String groupName = getAppCode();
    public static Boolean qconfigSwitch = getQconfigSwitch(); //true为使用新版

    private Map<String, String> configMap = new HashMap<>();
    public static ThreadLocal<MockQconfig> threadLocal = new ThreadLocal<>();

    public static MockQconfig getInstance() {
        if (threadLocal.get() == null) {
            threadLocal.set(new MockQconfig());
        }
        return threadLocal.get();
    }

    @Deprecated
    public static void setProperty(String fileName, String key, String value) {
        if (qconfigSwitch) {
            AegisLog.info("Old Qconfig setProperty will not support, Please use the new Version");
            assert false;
        }
        String filepath = QCONFIG_TEST_PATH + groupName + "/" + fileName;
        ConfigHandle.configMapSet(filepath, key, value);
    }

    public static void setProperty(Class clazz, String fieldName, String key, String value) {
        resetMapField(clazz, fieldName, key, value);
    }

    public static void setProperty(Object obj, String fieldName, String key, String value) {
        resetMapField(obj, fieldName, key, value);
    }

    public static void setProperty(Class clazz, String fieldName, Object newValue) {
        ResetFieldUtil.resetField(clazz, fieldName, newValue);
    }

    @Deprecated
    public static void setProperty(Class clazz, String fileName, String fieldName, String key, String value) {
        resetMapField(clazz, fileName, fieldName, key, value);
    }

    public static String getProperty(String fileName, String key) {
        String filepath = QCONFIG_TEST_PATH + groupName + "/" + fileName;
        Map<String, String> map = ConfigHandle.loadConfig(filepath);
        String value = map.get(key);
        return value;
    }

    public static void setContent(String fileName, String value) {
        String filepath = QCONFIG_TEST_PATH + groupName + "/" + fileName;
        String path = Thread.currentThread().getContextClassLoader().getResource(filepath).getPath();
        try {
            FileWriter fileWritter = new FileWriter(path, false);
            BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
            bufferWritter.write(value);
            bufferWritter.close();
            fileWritter.close();
        } catch (IOException e) {
            AegisLog.error(e);
        }
    }

    public static String getAppCode() {
        String appCode = null;
        try {
            Map<String, String> map = ConfigHandle.loadConfig("qunar-app.properties");
            appCode = map.get("name");
        } catch (Exception e) {
            AegisLog.error(e);
        }
        return appCode;
    }

    public static Boolean getQconfigSwitch() {
        Boolean qconfigSwitch = false; //默认可以使用旧版
        try {
            Map<String, String> map = ConfigHandle.loadConfig("qunar-env.properties");
            qconfigSwitch = Boolean.valueOf(map.get(QCONFIG_MOCK_SWITCH));
        } catch (Exception e) {
            AegisLog.error(e);
        }
        return qconfigSwitch;
    }

    public static Map<String, String> getMap(String fileName) {
        String filepath = QCONFIG_TEST_PATH + groupName + "/" + fileName;
        return ConfigHandle.loadConfig(filepath);
    }

    public static String getContent(String fileName) {
        String filepath = QCONFIG_TEST_PATH + groupName + "/" + fileName;
        String content = null;
        try {
            content = Files.toString(ConfigHandle.loadConfigToFile(filepath), Charset.forName("UTF-8"));
        } catch (IOException e) {
            AegisLog.error(e);
        }
        return content;
    }


    //use reflection to change the qconfig MapField
    public static void resetMapField(Class clazz, String fieldName, String key, String value) {
        try {
            //获取属性
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);

            //Filed类
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);

            //获取修饰符算子并去除final并添加static修饰符
            int modifiers = modifiersField.getInt(field);
            Boolean isVolitle = Modifier.isVolatile(modifiers);
            if (!isVolitle) {
                modifiers |= Modifier.VOLATILE;
            }
            modifiers &= ~Modifier.FINAL;
            modifiersField.setInt(field, modifiers);
            Boolean isStatic = Modifier.isStatic(modifiers);
            isVolitle = Modifier.isVolatile(modifiers);
            AegisLog.log(isVolitle);
            if (!isStatic) {
                // 更改私有属性的值
                if (qconfigSwitch) {
                    Map<String, String> fieldValue = (Map<String, String>) field.get(SpringUtil.getBean(clazz));
                    copyMap(fieldValue, getInstance().configMap);
                    getInstance().configMap.put(key, value);
                    field.set(SpringUtil.getBean(clazz), getInstance().configMap);
                } else {
                    ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:test.xml");
                    Map<String, String> fieldValue = (Map<String, String>) field.get(ac.getBean(clazz));
                    copyMap(fieldValue, getInstance().configMap);
                    getInstance().configMap.put(key, value);
                    field.set(ac.getBean(clazz), getInstance().configMap);
                }


            } else {
                //修改静态属性值
                Map<String, String> fieldValue = (Map<String, String>) field.get(clazz);
                copyMap(fieldValue, getInstance().configMap);
                getInstance().configMap.put(key, value);
                field.set(null, getInstance().configMap);
            }

        } catch (Exception e) {
            AegisLog.error(e);
        }
    }

    //use reflection to change the qconfig MapField ,参数是对象
    public static void resetMapField(Object obj, String fieldName, String key, String value) {
        try {
            //获取属性
            Field field = obj.getClass().getSuperclass().getDeclaredField(fieldName);
            field.setAccessible(true);

            //Filed类
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);

            //获取修饰符算子并去除final并添加static修饰符
            int modifiers = modifiersField.getInt(field);
            modifiers &= ~Modifier.FINAL;
            modifiersField.setInt(field, modifiers);
            Boolean isStatic = Modifier.isStatic(modifiers);

            if (!isStatic) {
                // 更改私有属性的值
                Map<String, String> fieldValue;
                if (qconfigSwitch) {
                    Object been = SpringUtil.getBean(obj.getClass().getSuperclass());
                    fieldValue = (Map<String, String>) field.get(been);
                } else {
                    ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:test.xml");
                    fieldValue = (Map<String, String>) field.get(ac.getBean(obj.getClass().getSuperclass()));
                }
                copyMap(fieldValue, getInstance().configMap);
                getInstance().configMap.put(key, value);
                field.set(obj, getInstance().configMap);

            } else {
                //修改静态属性值
                Map<String, String> fieldValue = (Map<String, String>) field.get(obj.getClass());
                copyMap(fieldValue, getInstance().configMap);
                getInstance().configMap.put(key, value);
                field.set(null, getInstance().configMap);
            }

        } catch (Exception e) {
            AegisLog.error(e);
        }
    }

    private static void copyMap(Map<String, String> fieldValue, Map<String, String> newFieldValue) {
        if (fieldValue == null) return;
        for (String k : fieldValue.keySet()) {
            newFieldValue.put(k, fieldValue.get(k));
        }
    }

    //use reflection to change the qconfig MapFieldfiled
    public static void resetMapField(Class clazz, String fileName, String fieldName, String key, String value) {
        try {
            //获取属性
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);

            //Filed类
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);

            //获取修饰符算子并去除final并添加static修饰符
            int modifiers = modifiersField.getInt(field);
            modifiers &= ~Modifier.FINAL;
            modifiersField.setInt(field, modifiers);
            Boolean isStatic = Modifier.isStatic(modifiers);
            getInstance().configMap = MockQconfig.getMap(fileName);
            getInstance().configMap.put(key, value);
            if (!isStatic) {
                // 更改私有属性的值
                if (qconfigSwitch) {
                    field.set(SpringUtil.getBean(clazz), getInstance().configMap);
                } else {
                    ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:test.xml");
                    field.set(ac.getBean(clazz), getInstance().configMap);
                }
            } else {
                //修改静态属性值
                field.set(null, getInstance().configMap);
            }

        } catch (Exception e) {
            AegisLog.error(e);
        }
    }

    public static void clear() {
        getInstance().configMap = new HashMap<>();
    }

    public static void mock() {
    }

}

